﻿namespace NTLib.Core.Log
{
    using Pool;
    using Reflection;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;

    /// <summary>
    /// 
    /// </summary>
    /// <seealso cref="NTLib.Core.Pool.PoolItem" />
    public class LogContainer : PoolItem
    {
        #region Fields

        private DateTime _time;
        private LogType _type;
        private string _origin;
        private string _emetor;
        private object _data;
        private object _context;
        private Type _dataType;
        private Type _contextType;

        #endregion

        #region Properties

        /// <summary>
        /// Gets the type of the data.
        /// Could be null if no data have been pass.
        /// </summary>
        /// <value>
        /// The type of the data.
        /// </value>
        public Type DataType
        {
            get { return this._dataType; }
        }

        /// <summary>
        /// Gets the type of the context.
        /// Could be null if no context have been pass.
        /// </summary>
        /// <value>
        /// The type of the context.
        /// </value>
        public Type ContextType
        {
            get { return this._contextType; }
        }

        /// <summary>
        /// Gets the type of the log.
        /// </summary>
        /// <value>
        /// The type of the log.
        /// </value>
        public LogType LogType
        {
            get { return this._type; }
        }

        /// <summary>
        /// Gets the origin of the log.
        /// class/Structure or assembly where the log came from.
        /// </summary>
        /// <value>
        /// The origin.
        /// </value>
        public string Origin
        {
            get { return this._origin; }
        }

        /// <summary>
        /// Gets the emetor of the log.
        /// method or exeception that emit this log.
        /// </summary>
        /// <value>
        /// The emetor.
        /// </value>
        public string Emetor
        {
            get { return this._emetor; }
        }

        /// <summary>
        /// Gets the data.
        /// </summary>
        /// <value>
        /// The data.
        /// </value>
        public object Data
        {
            get { return this._data; }
        }

        /// <summary>
        /// Gets the context.
        /// </summary>
        /// <value>
        /// The context.
        /// </value>
        public object Context
        {
            get { return this._context; }
        }

        /// <summary>
        /// Gets the UTC.
        /// </summary>
        public DateTime Utc
        {
            get { return this._time.ToUniversalTime(); }
        }

        #endregion

        #region Methods

        /// <summary>
        /// Initializes the specified type with all the necessary values
        /// </summary>
        /// <param name="type">The type of the log.</param>
        /// <param name="origin">The origin, class/Structure or assembly where the log came from.</param>
        /// <param name="emetor">The emetor, method or exeception that emit this log.</param>
        /// <param name="data">The data of the log itself. The object MUST be a class to prevent boxing problem.</param>
        /// <param name="context">The context,  different information usefull to have a log as precise as posible. The object MUST be a class to prevent boxing problem.</param>
        internal void Initialize(LogType type, string origin, string emetor, object data, object context = null)
        {
            this._time = DateTime.Now.ToUniversalTime();
            this._type = type;
            this._origin = origin;
            this._emetor = emetor;
            this._data = data;
            if (this._data != null)
                this._dataType = this._data.GetType();
            this._context = context;
            if (this._context != null)
                this._contextType = this._context.GetType();
        }

        /// <summary>
        /// Called when the pool item is reset.
        /// Everything MUST be initialized on this method.
        /// </summary>
        protected override void OnReset()
        {
            this._type = LogType.None;
            this._origin = null;
            this._emetor = null;

            if (this._data != null && this._data is IDisposable)
            {
                try
                {
                    ((IDisposable)this._data).Dispose();
                }
                catch
                {
                    // TODO : May be log in case of error ?
                }

            }

            this._data = null;
            if (this._context != null && this._context is IDisposable)
            {
                try
                {
                    ((IDisposable)this._context).Dispose();
                }
                catch
                {
                    // TODO : May be log in case of error ?
                }
            }

            this._context = null;
            this._dataType = null;
            this._contextType = null;
            this._time = default(DateTime);
        }

        /// <summary>
        /// Dispose the managed objects.
        /// This method will be called everytime the object is disposed.
        /// </summary>
        protected override void ManagedDispose()
        {
            base.ManagedDispose();

            OnReset();
        }

        /// <summary>
        /// Returns a <see cref="System.String" /> that represents this instance.
        /// </summary>
        /// <returns>
        /// A <see cref="System.String" /> that represents this instance.
        /// </returns>
        public override string ToString()
        {
            StringBuilder output = new StringBuilder(string.Format("[{2}] [{0}] - {1:u} -", this._type, this._time, this._time.Ticks));

            bool hasOrigin = !string.IsNullOrEmpty(this._origin);
            if (hasOrigin)
            {
                output.Append("[");
                output.Append(this._origin);
            }

            if (!string.IsNullOrEmpty(this._emetor))
            {
                if (hasOrigin)
                    output.Append(".");
                else
                    output.Append("[");
                output.Append(this._emetor);
                output.Append("]");
            }
            else if (hasOrigin)
                output.Append("]");

            if (this._context != null)
            {
                output.Append(" Context : ");
                output.Append(this._context);
            }
            if (this._data != null)
            {
                if (this._data is Exception)
                {
                    var ex = this._data as Exception;
                    var i = 0;
                    while (ex != null)
                    {
                        var strs = ex.ToString();
                        var strsLines = strs.Split('\n');

                        foreach (var s in strsLines)
                        {
                            output.Append(' ', i);
                            output.AppendLine(s);
                        }
                        ex = ex.InnerException;
                        i += 4;
                    }
                }
                else
                {
                    output.Append(" Data : ");

                    if (Data is IObjectDescriptor)
                    {
                        IObjectDescriptor obj = Data as IObjectDescriptor;
                        using (var dump = obj.Dump())
                            dump.JsonString(output, 1);
                    }
                    else
                        output.Append(this.Data);
                }
            }

            return output.ToString();
        }

        #endregion
    }
}
